package org.nlp2rdf.navigator.benchmark;

import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import org.aksw.commons.util.Files;
import org.aksw.commons.util.SerializationUtils;
import org.aksw.commons.util.experiments.ExperimentStatisticsCollector;
import org.aksw.commons.util.experiments.Static;
import org.aksw.commons.util.experiments.Table;
import org.aksw.commons.util.experiments.TableFormatter;
import org.aksw.commons.util.experiments.impl.GNUPlotRowFormatter;
import org.aksw.commons.util.experiments.impl.LatexRowFormatter;
import org.dllearner.core.EvaluatedDescription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;

/**
 * Created by Claus Stadler
 * Date: Oct 15, 2010
 * Time: 5:04:44 PM
 */
public class BenchmarkUtils
{
    private static final Logger logger = LoggerFactory.getLogger(BenchmarkUtils.class);
    
    /**
     * Obtains an InputStream from a command line argument:
     * If the fileName is null, empty or equals "-", STDIN is returned.
     * Otherwise an inputstream to the file is returned.
     *
     * @param fileName
     * @return
     * @throws java.io.IOException
     */
    public static InputStream obtainInputStream(String fileName)
            throws IOException
    {
        return (fileName == null || fileName.trim().isEmpty() || fileName.trim().equals("-"))
                ? System.in
                : new FileInputStream(new File(fileName));
    }


    public static BufferedReader obtainBufferedReader(String fileName)
            throws IOException
    {
        InputStream in = obtainInputStream(fileName);
        return new BufferedReader(new InputStreamReader(in));
    }

    public static List<Resource> readResources(String fileName)
        throws IOException
    {
        BufferedReader reader = obtainBufferedReader(fileName);

        String line;
        List<Resource> result = new ArrayList<Resource>();
        while((line = reader.readLine()) != null) {
            line = line.trim();
            if(line.isEmpty()) {
               continue;
            }

            if(!line.startsWith("http://")) {
                continue;
                //logger.info("Skipping line")
            }

            result.add(ResourceFactory.createResource(line));
        }

        return result;
    }


    public static Sample<Resource> readSample(String fileName)
        throws IOException
    {
        BufferedReader reader = obtainBufferedReader(fileName);

        String line;
        Sample<Resource> result = Sample.create();
        while((line = reader.readLine()) != null) {
            line = line.trim();

            if(line.trim().isEmpty()) {
               continue;
            }

            if(line.startsWith("-")) {
                result.getNegatives().add(ResourceFactory.createResource(line.substring(1).trim()));
            } else if(line.startsWith("+")) {
                result.getPositives().add(ResourceFactory.createResource(line.substring(1).trim()));                
            } else {
                logger.warn("Skipping line: " + line);
            }


        }

        return result;
    }


    /**
     *
     *
     * @param numPartitions
     * @param items
     * @param random
     * @param <T>
     * @return
     */
    public static <T> List<Set<T>> createRandomPartitions(int numPartitions, Collection<T> items, Random random)
    {
        List<T> src = new ArrayList<T>(items);
        Collections.shuffle(src, random);

        List<Set<T>> result = new ArrayList<Set<T>>();
        for(int i = 0; i < numPartitions; ++i)
            result.add(new HashSet<T>());

        int index = 0;
        for(T item : items) {
            result.get(index).add(item);
            index = (index + 1) % numPartitions;
        }

/*
        List<List<T>> partitions = Lists.partition(src, numPartitions);

        List<Set<T>> result = new ArrayList<Set<T>>();
        for(List<T> partition : partitions) {
            result.add(new HashSet<T>(partition));
        }
*/
        return result;
    }


    public static <T> List<Set<T>> fillUpRandomPartitions(Iterable<? extends Collection<?>> existing, int partitionSize, Collection<T> items, Random random)
    {
        List<T> src = new ArrayList<T>(items);
        Collections.shuffle(src, random);

        List<Set<T>> result = new ArrayList<Set<T>>();
        for(Collection<?> c : existing) {
            int remaining = Math.max(0, partitionSize - c.size());

            if(src.size() < remaining)
                throw new IllegalArgumentException("Not enough elements provided for fillup");

            List<T> tmp = src.subList(0, remaining);
            result.add(new HashSet<T>(tmp));
            tmp.clear();
        }

        return result;
    }

    public static <T> List<Sample<T>> createStratifiedKFolds(int numFolds, int foldSize, Collection<T> positives, Collection<T> negatives, Random random)
    {
        if(positives.size() < numFolds)
            throw new IllegalArgumentException("Cannot create " + numFolds + " folds with only " + positives.size() + " positives");

        int maxNumPositives = numFolds * foldSize;
        if(positives.size() > maxNumPositives)
            throw new IllegalArgumentException("Too little folds for too many positives");



        List<Sample<T>> result = new ArrayList<Sample<T>>();

        List<Set<T>> positivePartitions = createRandomPartitions(numFolds, positives, random);
        List<Set<T>> negativePartitions = fillUpRandomPartitions(positivePartitions, foldSize, negatives, random);

        assert positivePartitions.size() == negativePartitions.size();

        for(int i = 0; i < positivePartitions.size(); ++i) {
            result.add(new Sample(positivePartitions.get(i), negativePartitions.get(i)));
        }

        return result;
    }

    public static void add(ExperimentStatisticsCollector collector, int phaseId, EvaluatedDescription description)
    {
        collector.getMonitor("learningAccuracy", phaseId, Static.Units.PERCENTAGE).add(description.getAccuracy());        
    }

    public static void add(ExperimentStatisticsCollector collector, int phaseId, String prefix, SampleStats<?> stats)
    {
        collector.getMonitor(prefix + "Precision", phaseId, Static.Units.PERCENTAGE).add(stats.getPrecision());
        collector.getMonitor(prefix + "Recall", phaseId, Static.Units.PERCENTAGE).add(stats.getRecall());
        collector.getMonitor(prefix + "FMeasure", phaseId, Static.Units.PERCENTAGE).add(stats.getFMeasure());
    }


    public static void writeOutStatistics(String basePath, String experimentName, String runName, Table table)
        throws Exception
    {
        String baseName = basePath + "/" + experimentName + "/" + runName;
        writeOutStatistics(baseName, table);
    }

    public static void writeOutStatistics(String basePath, Table table)
            throws Exception
    {
        new File(basePath).getParentFile().mkdirs();

        // Serialize object
        File outFile = new File(basePath + ".xml");
        SerializationUtils.serializeXml(table, outFile, true);

        // Serialize latex
        TableFormatter latexTableFormatter = new LatexRowFormatter();
        String tableStr = latexTableFormatter.format(table);
        outFile = new File(basePath + ".latex");
        Files.writeToFile(outFile, tableStr, false);

        // Serialize gnu plu
        TableFormatter gnuplotTableFormatter = new GNUPlotRowFormatter();
        tableStr = gnuplotTableFormatter.format(table);
        outFile = new File(basePath + ".gnuplot");
        Files.writeToFile(outFile, tableStr, false);
    }
    
}
